package net.dubboclub.http.netty.servlet;

import com.alibaba.dubbo.common.utils.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.http.Cookie;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.security.Principal;
import java.util.*;

/**
 * @date: 2016/3/7.
 * @author:bieber.
 * @project:dubbo-side.
 * @package:com.alibaba.dubbo.remoting.http.netty.servlet.
 * @version:1.0.0
 * @fix:
 * @description: HttpRequest下层包装, 屏蔽下层的具体实现
 */
public class NettyHttpServletRequest extends NettyHttpServlet implements HttpServletRequest {

    private DefaultHttpRequest httpRequest;

    private Cookie[] cookies = new Cookie[0];

    private HttpPostRequestDecoder httpPostRequestDecoder;

    private QueryStringDecoder queryStringDecoder;

    private ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();

    private Channel channel;

    private boolean isPost = false;

    private String uri = null;

    public NettyHttpServletRequest(DefaultHttpRequest httpRequest, Channel channel) {
        this.httpRequest = httpRequest;
        this.channel = channel;
        init();
    }

    public DefaultHttpRequest getHttpRequest() {
        return httpRequest;
    }

    private void init() {
        uri = httpRequest.getUri();
        queryStringDecoder = new QueryStringDecoder(uri);
        if (getMethod().equalsIgnoreCase("post")) {
            isPost = true;
            httpPostRequestDecoder = new HttpPostRequestDecoder(httpRequest);
        }
        cookies = decodeCookies(httpRequest);
    }

    /**
     * 将request的内容片断添加到请求对象中
     *
     * @param httpContent
     */
    public void readContent(HttpContent httpContent) {
        if (isPost) {
            //httpPostRequestDecoder.offer(httpContent);
            byteBuf.writeBytes(httpContent.content());
        }
    }


    /**
     * 回收netty的byte buffer,防止内存泄露
     */
    public void completed() {
        //httpPostRequestDecoder.destroy();、
        assert byteBuf.refCnt()==1;
        byteBuf.release();
    }

    @Override
    public String getAuthType() {
        throw reject();
    }

    @Override
    public Cookie[] getCookies() {
        return cookies;
    }

    @Override
    public long getDateHeader(String s) {
        throw reject();
    }

    @Override
    public String getHeader(String s) {
        return httpRequest.headers().get(s);
    }

    @Override
    public Enumeration<String> getHeaders(final String s) {
        List<String> headers = httpRequest.headers().getAll(s);
        return new CollectionEnumeration<String>(headers);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        Set<String> names = httpRequest.headers().names();
        return new CollectionEnumeration<String>(names);
    }

    @Override
    public int getIntHeader(String s) {
        String value = httpRequest.headers().get(s);
        if (StringUtils.isEmpty(value)) {
            throw new IllegalAccessError("not found header [" + s + "].");
        }
        return Integer.parseInt(value);
    }

    @Override
    public String getMethod() {
        return httpRequest.getMethod().name();
    }

    @Override
    public String getPathInfo() {
        return queryStringDecoder.path();
    }

    @Override
    public String getPathTranslated() {
        return getPathInfo();
    }

    @Override
    public String getContextPath() {
        throw reject();
    }

    @Override
    public String getQueryString() {
        if (uri.indexOf("?") >= 0) {
            return uri.substring(uri.indexOf("?") + 1);
        }
        return null;
    }

    @Override
    public String getRemoteUser() {
        throw reject();
    }

    @Override
    public boolean isUserInRole(String s) {
        throw reject();
    }

    @Override
    public Principal getUserPrincipal() {
        throw reject();
    }

    @Override
    public String getRequestedSessionId() {
        throw reject();
    }

    @Override
    public String getRequestURI() {
        return queryStringDecoder.path();
    }

    @Override
    public StringBuffer getRequestURL() {
        return new StringBuffer(queryStringDecoder.path());
    }

    @Override
    public String getServletPath() {
        throw reject();
    }

    @Override
    public HttpSession getSession(boolean b) {
        throw reject();
    }

    @Override
    public HttpSession getSession() {
        throw reject();
    }

    @Override
    public boolean isRequestedSessionIdValid() {
        throw reject();
    }

    @Override
    public boolean isRequestedSessionIdFromCookie() {
        throw reject();
    }

    @Override
    public boolean isRequestedSessionIdFromURL() {
        throw reject();
    }

    @Override
    public boolean isRequestedSessionIdFromUrl() {
        throw reject();
    }

    @Override
    public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException {
        throw reject();
    }

    @Override
    public void login(String s, String s1) throws ServletException {
        throw reject();
    }

    @Override
    public void logout() throws ServletException {
        throw reject();
    }

    @Override
    public Collection<Part> getParts() throws IOException, ServletException {
       /* if (isPost) {
            List<InterfaceHttpData> interfaceHttpDataList = httpPostRequestDecoder.getBodyHttpDatas();
            List<Part> parts = new ArrayList<Part>();
            for (InterfaceHttpData interfaceHttpData : interfaceHttpDataList) {
                Part part = new NettyHttpServletPart((HttpData) interfaceHttpData);
                parts.add(part);
            }
            return parts;
        }
        return Collections.EMPTY_LIST;*/
        throw reject();
    }

    @Override
    public Part getPart(String s) throws IOException, ServletException {
      /*  if (isPost) {
            HttpData httpData = (HttpData) httpPostRequestDecoder.getBodyHttpData(s);
            return new NettyHttpServletPart(httpData);
        }
        return null;*/
        throw reject();
    }

    @Override
    public Object getAttribute(String s) {
        /*if (isPost) {
            InterfaceHttpData interfaceHttpData = httpPostRequestDecoder.getBodyHttpData(s);
            if (io.netty.handler.codec.http.multipart.Attribute.class.isAssignableFrom(interfaceHttpData.getClass())) {
                Attribute attribute = (Attribute) interfaceHttpData;
                try {
                    return attribute.getValue();
                } catch (IOException e) {
                    return null;
                }
            }
        }

        return null;*/
        throw reject();
    }

    @Override
    public Enumeration<String> getAttributeNames() {
       /* if (isPost) {
            List<InterfaceHttpData> interfaceHttpDataList = httpPostRequestDecoder.getBodyHttpDatas();
            List<String> names = new ArrayList<String>();
            for (InterfaceHttpData httpData : interfaceHttpDataList) {
                names.add(httpData.getName());
            }
            return new CollectionEnumeration<String>(names);
        }
        return new CollectionEnumeration<String>(Collections.EMPTY_LIST);*/
        throw reject();
    }

    @Override
    public String getCharacterEncoding() {
        throw reject();
    }

    @Override
    public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
        throw reject();
    }

    @Override
    public int getContentLength() {
        String contentLength = httpRequest.headers().get(HttpHeaders.Names.CONTENT_LENGTH);
        if (StringUtils.isEmpty(contentLength)) {
            return 0;
        }
        return Integer.parseInt(contentLength);
    }

    @Override
    public String getContentType() {
        return httpRequest.headers().get(HttpHeaders.Names.CONTENT_TYPE);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
       /* if (isPost) {
            NettyHttpServletInputStream nettyHttpServletInputStream = new NettyHttpServletInputStream(httpPostRequestDecoder.getBodyHttpDatas());
            return nettyHttpServletInputStream;
        }*/
        return new NettyHttpServletInputStream(byteBuf);
    }

    @Override
    public String getParameter(String s) {
        List<String> params = queryStringDecoder.parameters().get(s);
        if (params == null || params.size() <= 0) {
            return null;
        }
        return params.get(0);
    }

    @Override
    public Enumeration<String> getParameterNames() {
        if (queryStringDecoder == null) {
            return null;
        }
        Set<String> names = queryStringDecoder.parameters().keySet();
        return new CollectionEnumeration(names);
    }

    @Override
    public String[] getParameterValues(String s) {
        List<String> values = queryStringDecoder.parameters().get(s);
        if (values == null) {
            return new String[0];
        } else {
            return values.toArray(new String[0]);
        }
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> params = new HashMap<String, String[]>();
        Map<String, List<String>> originParams = queryStringDecoder.parameters();
        for (Map.Entry<String, List<String>> entry : originParams.entrySet()) {
            params.put(entry.getKey(), entry.getValue().toArray(new String[0]));
        }
        return params;
    }

    @Override
    public String getProtocol() {
        return httpRequest.getProtocolVersion().protocolName();
    }

    @Override
    public String getScheme() {
        return getProtocol();
    }

    @Override
    public String getServerName() {
        InetSocketAddress socketAddress = (InetSocketAddress) channel.localAddress();
        return socketAddress.getHostName();
    }

    @Override
    public int getServerPort() {
        InetSocketAddress socketAddress = (InetSocketAddress) channel.localAddress();
        return socketAddress.getPort();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public String getRemoteAddr() {
        InetSocketAddress socketAddress = (InetSocketAddress) channel.remoteAddress();
        return socketAddress.getHostName();
    }

    @Override
    public String getRemoteHost() {
        return getRemoteAddr();
    }

    @Override
    public void setAttribute(String s, Object o) {
        throw reject();
    }

    @Override
    public void removeAttribute(String s) {
        throw reject();
    }

    @Override
    public Locale getLocale() {
        throw reject();
    }

    @Override
    public Enumeration<Locale> getLocales() {
        throw reject();
    }

    @Override
    public boolean isSecure() {
        throw reject();
    }

    @Override
    public RequestDispatcher getRequestDispatcher(String s) {
        throw reject();
    }

    @Override
    public String getRealPath(String s) {
        throw reject();
    }

    @Override
    public int getRemotePort() {
        InetSocketAddress socketAddress = (InetSocketAddress) channel.remoteAddress();
        return socketAddress.getPort();
    }

    @Override
    public String getLocalName() {
        return getServerName();
    }

    @Override
    public String getLocalAddr() {
        return getServerName();
    }

    @Override
    public int getLocalPort() {
        return getServerPort();
    }

    @Override
    public ServletContext getServletContext() {
        throw reject();
    }

    @Override
    public AsyncContext startAsync() throws IllegalStateException {
        throw reject();
    }

    @Override
    public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
        throw reject();
    }

    @Override
    public boolean isAsyncStarted() {
        throw reject();
    }

    @Override
    public boolean isAsyncSupported() {
        throw reject();
    }

    @Override
    public AsyncContext getAsyncContext() {
        throw reject();
    }

    @Override
    public DispatcherType getDispatcherType() {
        throw reject();
    }


}
